package com.fhsk.file.manager.controller;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.URLUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fhsk.file.business.entity.bo.attachment.AttachmentBo;
import com.fhsk.file.business.entity.bo.attachment.AttachmentConditionBo;
import com.fhsk.file.business.entity.dto.attachment.AttachmentDto;
import com.fhsk.file.business.entity.vo.attachment.AttachmentVo;
import com.fhsk.file.business.entity.vo.user.UserVo;
import com.fhsk.file.business.service.IAttachmentService;
import com.fhsk.file.business.service.IUserService;
import com.fhsk.file.common.constants.Constants;
import com.fhsk.file.common.enums.ResourceTypeEnum;
import com.fhsk.file.common.enums.RoleTypeEnum;
import com.fhsk.file.common.exception.UnifiedException;
import com.fhsk.file.common.vo.AjaxResult;
import com.google.common.collect.Lists;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 附件管理
 */
@Slf4j
@RestController
@RequestMapping("/attachment")
@Validated
@Api(value = "", tags = "附件管理")
public class AttachmentController {

    @Autowired
    private IAttachmentService attachmentService;

    @Resource
    private IUserService userService;

    @Value("${filepath.windows}")
    private String windowsPath;

    @Value("${filepath.linux}")
    private String linuxPath;

//    /**
//     * 新增附件
//     *
//     * @param attachmentBo
//     * @return
//     */
//    @ApiOperation("新增附件")
//    @PostMapping("/add")
//    public AjaxResult addAttachment(@RequestBody AttachmentBo attachmentBo) {
//        if (StringUtils.isEmpty(attachmentBo.getFileOid())) {
//            return AjaxResult.fail("请生成oid");
//        }
//        return attachmentService.addAttachment(attachmentBo);
//    }

    /**
     * 查询附件列表
     *
     * @param condition
     * @return
     */
    @ApiOperation("查询附件列表")
    @PostMapping("/list")
    public AjaxResult getAttachmentListByCondition(@RequestBody AttachmentConditionBo condition) {
        UserVo currentUser = userService.getCurrentUser();
        if (null != currentUser && RoleTypeEnum.ADMIN.getCode().equals(currentUser.getRoleType())) {
            // 普通管理员只能查看自己的文件
            condition.setCreateBy(currentUser.getOid());
        }
        Map<String, Object> attachmentMap = attachmentService.getAttachmentListByCondition(condition);
        return AjaxResult.success(attachmentMap);
    }

//    /**
//     * 修改附件表
//     *
//     * @param attachmentBo
//     * @return
//     */
//    @ApiOperation("修改附件")
//    @PostMapping("/update")
//    public AjaxResult updateAttachment(@Validated @RequestBody AttachmentBo attachmentBo) {
//        if (null == attachmentBo.getId()) {
//            return AjaxResult.fail("附件表id不能为空");
//        }
//        return attachmentService.updateAttachment(attachmentBo);
//    }

    /**
     * 查询附件详情
     *
     * @param fileOid
     * @return
     */
    @ApiOperation("查询附件详情")
    @GetMapping("/detail")
    public AjaxResult getDetail(@RequestParam("fileOid") String fileOid) {
        AttachmentVo vo = attachmentService.getDetail(fileOid);
        return AjaxResult.success(vo);
    }

    /**
     * 删除附件
     *
     * @param fileOid
     * @return
     */
    @ApiOperation("删除附件")
    @PostMapping("/delete")
    public AjaxResult delete(@RequestParam("fileOid") String fileOid) {
        AttachmentVo attachmentVo = attachmentService.getDetail(fileOid);
        if (null == attachmentVo) {
            return AjaxResult.fail("附件不存在");
        }

        // 删除文件
        String filePath = (isWindows() ? windowsPath : linuxPath) + attachmentVo.getFilePath();
        FileUtil.del(filePath);

        if (attachmentService.removeById(attachmentVo.getId())) {
            return AjaxResult.success("删除成功");
        }
        return AjaxResult.fail("删除失败");
    }

    /**
     * 删除附件
     *
     * @param fileOids
     * @return
     */
    @ApiOperation("删除附件")
    @PostMapping("/batchDelete")
    public AjaxResult batchDelete(@RequestParam("fileOids") List<String> fileOids) {
        LambdaQueryWrapper<AttachmentDto> lqw = new LambdaQueryWrapper<>();
        lqw.in(AttachmentDto::getFileOid, fileOids);
        List<AttachmentDto> attachmentDtos = attachmentService.list(lqw);
        if (CollectionUtil.isNotEmpty(attachmentDtos)) {
            for (AttachmentDto attachmentDto : attachmentDtos) {
                // 删除文件
                String filePath = (isWindows() ? windowsPath : linuxPath) + attachmentDto.getFilePath();
                FileUtil.del(filePath);
            }

            // 删除记录
            List<Long> ids = attachmentDtos.stream().map(t -> t.getId()).collect(Collectors.toList());
            attachmentService.removeByIds(ids);
        }

        return AjaxResult.success();
    }

    public static void main(String[] args) {
        String uuidName = IdUtil.simpleUUID() + ".PDF";
        System.out.println(uuidName);

        //原始文件名
        String originalName = uuidName;
        originalName = ReUtil.replaceAll(originalName, Constants.FILE_NAME_RE, "");
        //获取文件格式
        String fileExtName = Objects.requireNonNull(FileUtil.extName(originalName)).toLowerCase();

        System.out.println(fileExtName);
    }

    /**
     * 上传附件
     *
     * @param file
     * @return
     * @throws IOException
     */
    @ApiOperation("上传附件")
    @PostMapping("/upload")
    public AjaxResult upload(@RequestParam(value = "file") MultipartFile file, @RequestParam(value = "image", required = false) MultipartFile img, AttachmentBo attachmentBo) throws Exception {
        return attachmentService.upload(file, img, attachmentBo);
    }

    /**
     * 批量上传文件
     *
     * @param files
     * @param img
     * @param attachmentBo
     * @return
     * @throws IOException
     */
    @ApiOperation("批量上传文件")
    @PostMapping("/uploadFiles")
    public AjaxResult uploadFiles(@RequestParam(value = "files") MultipartFile[] files, @RequestParam(value = "image", required = false) MultipartFile img, AttachmentBo attachmentBo) {
        if (null == files || files.length == 0) {
            return AjaxResult.fail("文件不能为空");
        }

        List<AttachmentVo> attachmentVos = Lists.newArrayList();
        for (MultipartFile file : files) {
            AjaxResult upload = null;
            try {
                upload = upload(file, img, attachmentBo);
            } catch (Exception e) {
                log.error("文件{}上传出错{}", file.getOriginalFilename(), e.getMessage());
            }

            if (!upload.getSuccess()) {
                continue;
            }

            AttachmentVo attachmentVo = (AttachmentVo) upload.getData();
            attachmentVos.add(attachmentVo);
        }

        return AjaxResult.success(attachmentVos);
    }

    /**
     * 下载文件
     *
     * @param fileOid
     * @param response
     */
    @GetMapping("/download/{fileOid}")
    @ApiOperation(value = "下载文件")
    public void download(@PathVariable("fileOid") String fileOid, HttpServletResponse response) {

        // 获取附件信息
        AttachmentVo attachment = attachmentService.getDetail(fileOid);
        if (attachment == null) {
            throw new UnifiedException("附件不存在");
        }

        // 文件名
        String fileName = attachment.getOriginalName();
        // 文件相对路径
        String fileRelativePath = attachment.getFilePath();
        fileRelativePath = isWindows() ? windowsPath + fileRelativePath : linuxPath + fileRelativePath;

        Long fileSize = attachment.getFileSize();
        fileSize = fileSize * 1024;

        // 进行下载
        response.setContentLengthLong(new File(fileRelativePath).length());
        //response.setHeader(HttpHeaders.CONTENT_LENGTH,fileSize+"");
        response.setContentType("application/octet-stream; charset=utf-8");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + URLUtil.encode(fileName));

        InputStream inputStream = null;
        final ServletOutputStream outputStream;
        try {
            inputStream = new FileInputStream(fileRelativePath);
            outputStream = response.getOutputStream();

            //写出
            byte[] b = new byte[1024];
            int len;
            //从输入流中读取一定数量的字节，并将其存储在缓冲区字节数组中，读到末尾返回-1
            while ((len = inputStream.read(b)) > 0) {
                outputStream.write(b, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new UnifiedException("文件下载失败");
        } finally {
            IoUtil.close(inputStream);
        }
    }

    /**
     * 文件预览
     *
     * @param fileOid
     * @throws IOException
     */
    @GetMapping("/preview/{fileOid}")
    @ApiOperation(value = "文件预览")
    public void preview(@PathVariable("fileOid") String fileOid, HttpServletResponse response) {
        final byte[] preview = filePreview(fileOid);
        if (preview == null) {
            throw new UnifiedException("文件预览失败");
        }
        response.setContentLength(preview.length);
        response.setHeader("Accept-Ranges", "bytes");
        final ServletOutputStream outputStream;
        try {
            outputStream = response.getOutputStream();
        } catch (IOException e) {
            throw new UnifiedException("文件预览失败");
        }

        IoUtil.write(outputStream, false, preview);
    }

    /**
     * 读取文件
     *
     * @param fileOid
     * @throws IOException
     */
    private byte[] filePreview(String fileOid) {
//        //首先判断文件是否正在转换,如果正在转换则提示用户,预览文件还未转换完成
//        String lockKey = RedisKeyEnum.PREVIEWLOCK.getValue() + fileOid;
//        Object isConverting = redisComponent.get(lockKey);
//        if (isConverting != null && "true".equals(isConverting.toString())) {
//            throw new UnifiedException("预览文件正在生成中!请稍后再预览!");
//        }
        AttachmentVo attachment = attachmentService.getDetail(fileOid);
        if (null == attachment) {
            return null;
        }
        String fileRelativePath = attachment.getPreviewPath();

        String filePath = isWindows() ? windowsPath + fileRelativePath : linuxPath + fileRelativePath;
        BufferedInputStream inputStream = FileUtil.getInputStream(filePath);
        //进行读取数据流 内部强制关闭流
        final byte[] bytes = IoUtil.readBytes(inputStream, true);
        return bytes;
    }

    /**
     * 判断服务器是linux还是windows
     *
     * @return 是否是windows系统
     * @author luojian
     */
    private boolean isWindows() {
        return System.getProperties().getProperty("os.name").toLowerCase().contains("windows");
    }

    /**
     * 获取服务器文件基础路径
     *
     * @param fileExtName
     * @return 文件基础路径，列如：/20221015/pdf/
     */
    private String getRelativeDir(String fileExtName) {
        String now = DateUtil.format(DateUtil.date(), "yyyyMMdd");
        String type = getFileType(fileExtName);
        return File.separator + now + File.separator + type + File.separator;
    }

    /**
     * 获取文件类型
     *
     * @param extName
     * @return
     */
    private String getFileType(String extName) {
//        List<DictionaryDataDto> list = dictionaryDataService.list((new LambdaQueryWrapper<DictionaryDataDto>())
//                .eq(DictionaryDataDto::getDictValue, extName)
//                .eq(DictionaryDataDto::getDictLabel, extName));
//        if (!CollectionUtil.isEmpty(list)) {
//            return list.get(0).getDictType();
//        } else {
//            return "other";
//        }
        return ResourceTypeEnum.PDF.getName();
    }

    private boolean isDoc(String fileExtName) {
        return ReUtil.isMatch("doc|docx", fileExtName);
    }

    private boolean isPpt(String fileExtName) {
        return ReUtil.isMatch("ppt|pptx", fileExtName);
    }

    private boolean isPreview(String fileExtName) {
        return ReUtil.isMatch("doc|docx|ppt|pptx|pdf", fileExtName);
    }
}
